package com.google.sky.mvc;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.sky.Board;
import com.google.sky.MoveResolver;
import com.google.sky.domain.*;
import com.google.sky.dto.BoardSettings;
import com.google.sky.dto.GameStateDto;
import com.google.sky.dto.LoadingResult;
import com.google.sky.flow.GameController;
import com.google.sky.flow.MoveController;
import com.google.sky.flow.MoveControllerImpl;
import com.google.sky.register.Settings;
import com.google.sky.register.SettingsImpl;
import com.google.sky.util.ImplementationsFinder;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileWriter;
import java.util.List;

@Controller
@RequestMapping("/")
public class GameMVCController {

    @Autowired(required = true)
    private ApplicationContext context;

    @Value("${upload.location}")
    private String libsLocation;

    @RequestMapping("/run")
    public String main() {
        return "main";
    }

    @RequestMapping(value = "/start", method = RequestMethod.POST, consumes = "application/json",
            produces = "application/json"
           )
    @ResponseBody
    public String initGame(@RequestBody PlayerStartConfig config) {

        GameController gameController = context.getBean(GameController.class);
        SettingsImpl settings = (SettingsImpl) context.getBean(Settings.class);
        settings.setWidth(config.getW());
        settings.setHeight(config.getH());
        MoveControllerImpl moveController1 = new MoveControllerImpl(Mark.X);
        moveController1.setMoveResolver(settings.getResolverById(config.getP1()));
        MoveControllerImpl moveController2 = new MoveControllerImpl(Mark.O);
        moveController2.setMoveResolver(settings.getResolverById(config.getP2()));
        gameController.run(moveController1, moveController2, config.getH(), config.getW());
        return "1";
    }

    @RequestMapping(value = "/initField", method = RequestMethod.POST,
            consumes = "application/json",
            produces = "application/json")
    @ResponseBody

    public BoardSettings initGame(@RequestBody BoardSettings boardSettings) {
        int height, width;
        try {
            height = Integer.parseInt(boardSettings.getHeight());
            width = Integer.parseInt(boardSettings.getWidth());
            if (height <= 0 || width <= 0) {
                throw new RuntimeException("Negative size");
            }
        } catch (Exception e) {
            return BoardSettings.withError(1, e.getMessage());
        }
        SettingsImpl settings = context.getBean(SettingsImpl.class);
        settings.setHeight(height);
        settings.setWidth(width);
        return BoardSettings.widthSize(height, width);
    }

    @RequestMapping(value = "/uploadFile", method = RequestMethod.POST, produces = "application/json")
    @ResponseBody
    public synchronized List<TeamDto>  loadLibrary(@RequestParam("teamName") String name, @RequestParam("file") MultipartFile file) {
        File libDir = new File(libsLocation);
        if (!libDir.exists()) {
            libDir.mkdirs();
        }
        List<TeamDto> teamDtos = getTeams();
        if (teamDtos!=null){
            for (TeamDto teamDto: teamDtos){
                if (teamDto.getName().equals(name)){
                    return teamDtos;
                }
            }
        }
        String fileName = String.valueOf(System.currentTimeMillis());
        File jarFile = new File(libDir, fileName + ".jar");
        FileWriter fw=null;
        try {
            file.transferTo(jarFile);
            File nameFile = new File(libDir, fileName + ".name");
            fw = new FileWriter(nameFile);
            fw.append(name);
        } catch (Exception e) {
            LoadingResult loadingResult = new LoadingResult();
            loadingResult.setErrorMessage(e.getMessage());
            loadingResult.setErrorCode("1");
            return Lists.newArrayList();
        }   finally {
              if (fw!=null){
                  IOUtils.closeQuietly(fw);
              }
        }
        LoadingResult loadingResult = new LoadingResult();
        loadingResult.setErrorCode("0");
        return getTeams();
    }

    @ResponseBody
    @RequestMapping(value = "/getState", method = RequestMethod.POST, produces = "application/json")
    public GameStateDto getState() {
        GameController gameController = context.getBean(GameController.class);
        GameStateDto res = new GameStateDto();
        BoardImpl board = (BoardImpl) gameController.getBoard();
        if (board != null) {
            res.setCellStateList(Lists.newArrayList(board.getCellStates()));
            res.setH(board.getHeight());
            res.setW(board.getWidth());
        }
        res.setFirstPlayerTime(toTime(gameController.getPlayer1Time()));
        res.setSecondPlayerTime(toTime(gameController.getPlayer2Time()));
        res.setFinished(!gameController.isRunning());
        if (res.isFinished()){
            res.setWinner(gameController.getWinner());
        }
        return res;
    }

    private String toTime(Long mils){
        Long hours = mils/60/60/1000;
        Long mins = (mils - hours*60*60*1000)/60/1000;
        Long sec = (mils - hours*60*60*1000 - mins*60*1000)/1000;
        Long msec = (mils - hours*60*60*1000 - mins*60*1000 - sec*1000);
        return String.format("%02d:%02d:%02d:%03d",hours, mins,sec,msec);
    }

    @ResponseBody
    @RequestMapping(value = "/getTeams", method = RequestMethod.POST, produces = "application/json")
    public synchronized List<TeamDto> getTeams(){
        Settings settings = context.getBean(Settings.class);
        List<MoveResolverMeta> metas = ImplementationsFinder.findImplementations(settings.getUploadPath());
        List<TeamDto> res = Lists.newArrayList();
        for (int i=0;i<metas.size();i++){
             res.add(new TeamDto(i, metas.get(i).getTeamName()));
        }
        return res;
    }

}
